home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / MP3 / Id.php
PHP Script  |  2004-10-01  |  27KB  |  977 lines

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This code is released under the GNU LGPL Go read it over here:       |
  9. // | http://www.gnu.org/copyleft/lesser.html                              |
  10. // +----------------------------------------------------------------------+
  11. // | Authors: Sandy McArthur Jr. <Leknor@Leknor.com>                      |
  12. // +----------------------------------------------------------------------+
  13. //
  14. // $Id: Id.php,v 1.7 2004/06/12 10:43:16 alexmerz Exp $
  15. //
  16.  
  17. // Uncomment the folling define if you want the class to automatically
  18. // read the MPEG frame info to get bitrate, mpeg version, layer, etc.
  19. //
  20. // NOTE: This is needed to maintain pre-version 1.0 behavior which maybe
  21. // needed if you are using info that is from the mpeg frame. This includes
  22. // the length of the song.
  23. //
  24. // This is discouraged because it will siginfincantly lengthen script
  25. // execution time if all you need is the ID3 tag info.
  26. // define('ID3_AUTO_STUDY', true);
  27.  
  28. // Uncomment the following define if you want tons of debgging info.
  29. // Tip: make sure you use a <PRE> block so the print_r's are readable.
  30. // define('ID3_SHOW_DEBUG', true);
  31.  
  32. require_once "PEAR.php" ;
  33.  
  34. /**
  35. * File not opened
  36. * @const PEAR_MP3_ID_FNO
  37. */
  38. define('PEAR_MP3_ID_FNO', 1);
  39.  
  40. /**
  41. * Read error
  42. * @const PEAR_MP3_ID_RE
  43. */
  44. define('PEAR_MP3_ID_RE', 2);
  45.  
  46. /**
  47. * Tag not found
  48. * @const PEAR_MP3_ID_TNF 
  49. */
  50. define('PEAR_MP3_ID_TNF', 3);
  51.  
  52. /**
  53. * File is not a MP3 file (corrupted?)
  54. * @const PEAR_MP3_ID_NOMP3 
  55. */
  56. define('PEAR_MP3_ID_NOMP3', 4);
  57.  
  58. /**
  59.  * A Class for reading/writing MP3 ID3 tags
  60.  * 
  61.  * Note: This code doesn't try to deal with corrupt mp3s. So if you get
  62.  * incorrect length times or something else it may be your mp3. To fix just
  63.  * re-enocde from the CD. :~)
  64.  *
  65.  * eg:
  66.  * require_once("MP3/Id.php");
  67.  * $file = "Some Song.mp3";
  68.  *
  69.  * $id3 = &new MP3_Id();
  70.  * $id3->read($file);
  71.  * print_r($id3);
  72.  *
  73.  * echo $id3->getTag('artists');
  74.  *
  75.  * $id3->comment = "Be gentle with that file.";
  76.  * $id3->write();
  77.  * $id3->read($file);
  78.  * print_r($id3 );
  79.  * 
  80.  * @package MP3_Id
  81.  * @author Sandy McArthur Jr. <Leknor@Leknor.com>
  82.  * @version $Version$
  83.  */
  84. class MP3_Id {
  85.  
  86.     /**
  87.     * mp3/mpeg file name
  88.     * @var boolean
  89.     */
  90.     var $file = false;      
  91.     /**
  92.     * ID3 v1 tag found? (also true if v1.1 found)
  93.     * @var boolean
  94.     */
  95.     var $id3v1 = false;     
  96.     /**
  97.     * ID3 v1.1 tag found? 
  98.     * @var boolean
  99.     */    
  100.     var $id3v11 = false;
  101.     /**
  102.     * ID3 v2 tag found? (not used yet)
  103.     * @var boolean
  104.     */        
  105.     var $id3v2 = false;
  106.  
  107.     // ID3v1.1 Fields:
  108.     /**
  109.     * trackname
  110.     * @var string
  111.     */        
  112.     var $name = '';
  113.     /**
  114.     * artists
  115.     * @var string
  116.     */            
  117.     var $artists = '';
  118.     /**
  119.     * album
  120.     * @var string
  121.     */                
  122.     var $album = '';
  123.     /**
  124.     * year
  125.     * @var string
  126.     */                
  127.     var $year = '';  
  128.     /**
  129.     * comment
  130.     * @var string
  131.     */                
  132.     var $comment = '';
  133.     /**
  134.     * track number
  135.     * @var integer
  136.     */                
  137.     var $track = 0;
  138.     /**
  139.     * genre name
  140.     * @var string
  141.     */
  142.     var $genre = '';
  143.     /**
  144.     * genre number
  145.     * @var integer
  146.     */                    
  147.     var $genreno = 255;
  148.  
  149.     // MP3 Frame Stuff
  150.     /**
  151.     * Was the file studied to learn more info?
  152.     * @var boolean
  153.     */
  154.     var $studied = false;
  155.     
  156.     /**
  157.     * version of mpeg
  158.     * @var integer
  159.     */    
  160.     var $mpeg_ver = 0;
  161.     /**
  162.     * version of layer
  163.     * @var integer
  164.     */        
  165.     var $layer = 0;
  166.     /**
  167.     * version of bitrate
  168.     * @var integer
  169.     */        
  170.     var $bitrate = 0;
  171.     /**
  172.     * Frames are crc protected?
  173.     * @var boolean
  174.     */            
  175.     var $crc = false;
  176.     /**
  177.     * frequency
  178.     * @var integer
  179.     */                
  180.     var $frequency = 0;
  181.     /**
  182.     * Frames padded
  183.     * @var boolean
  184.     */                
  185.     var $padding = false;
  186.     /**
  187.     * private bit set
  188.     * @var boolean
  189.     */                    
  190.     var $private = false;
  191.     /**
  192.     * Mode (Stero etc)
  193.     * @var string
  194.     */                    
  195.     var $mode = '';
  196.     /**
  197.     * Copyrighted
  198.     * @var string
  199.     */                        
  200.     var $copyright = false; 
  201.     /**
  202.     * On Original Media? (never used)
  203.     * @var boolean
  204.     */                        
  205.     var $original = false;
  206.     /**
  207.     * Emphasis (also never used)
  208.     * @var boolean
  209.     */                        
  210.     var $emphasis = '';     
  211.     /**
  212.     * Bytes in file 
  213.     * @var integer
  214.     */
  215.     var $filesize = -1;
  216.     /**
  217.     * Byte at which the first mpeg header was found
  218.     * @var integer
  219.     */                            
  220.     var $frameoffset = -1;
  221.     /**
  222.     * length of mp3 format hh:ss
  223.     * @var string
  224.     */
  225.     var $length = false; 
  226.     /**
  227.     * length of mp3 in seconds
  228.     * @var string
  229.     */                            
  230.     var $lengths = false;
  231.  
  232.     /**
  233.     * if any errors they will be here
  234.     * @var string
  235.     */
  236.     var $error = false;    
  237.  
  238.     /**
  239.     * print debugging info?
  240.     * @var boolean
  241.     */
  242.     var $debug = false;
  243.     /**
  244.     * print debugg
  245.     * @var string
  246.     */    
  247.     var $debugbeg = '<DIV STYLE="margin: 0.5 em; padding: 0.5 em; border-width: thin; border-color: black; border-style: solid">';
  248.     /**
  249.     * print debugg
  250.     * @var string
  251.     */
  252.     var $debugend = '</DIV>';
  253.  
  254.     /*
  255.      * creates a new id3 object
  256.      * and loads a tag from a file.
  257.      *
  258.      * @param string    $study  study the mpeg frame to get extra info like bitrate and frequency
  259.      *                          You should advoid studing alot of files as it will siginficantly
  260.      *                          slow this down.
  261.      * @access public
  262.      */
  263.     function MP3_Id($study = false) {
  264.         if(defined('ID3_SHOW_DEBUG')) $this->debug = true;
  265.         $this->study=($study || defined('ID3_AUTO_STUDY'));
  266.  
  267.     } // id3()
  268.  
  269.     /**
  270.     * reads the given file and parse it
  271.     *
  272.     * @param    string  $file the name of the file to parse
  273.     * @return   mixed   PEAR_Error on error
  274.     * @access   public
  275.     */
  276.     function read( $file="") {
  277.         if ($this->debug) print($this->debugbeg . "id3('$file')<HR>\n");
  278.  
  279.         if(!empty($file))$this->file = $file;
  280.         if ($this->debug) print($this->debugend);
  281.  
  282.         return $this->_read_v1();
  283.     }
  284.  
  285.     /**
  286.     * sets a field
  287.     * 
  288.     * possible names of tags are:
  289.     * artists   - Name of band or artist
  290.     * album     - Name of the album
  291.     * year      - publishing year of the album or song
  292.     * comment   - song comment
  293.     * track     - the number of the track
  294.     * genre     - genre of the song
  295.     * genreno   - Number of the genre
  296.     *
  297.     * @param    mixed   $name   Name of the tag to set or hash with the key as fieldname
  298.     * @param    mixed   $value  the value to set 
  299.     *
  300.     * @access   public
  301.     */
  302.     function setTag($name, $value) {
  303.         if( is_array($name)) {
  304.             foreach( $name as $n => $v) {
  305.                 $this -> $n = $v ;
  306.                 }
  307.         } else {
  308.             $this -> $name = $value ;        
  309.         }
  310.     }
  311.     
  312.     /**
  313.     * get the value of a tag
  314.     * 
  315.     * @param    string  $name       the name of the field to get
  316.     * @param    mixed   $default    returned if the field not exists
  317.     * 
  318.     * @return   mixed   The value of the field
  319.     * @access   public 
  320.     * @see      setTag
  321.     */
  322.     function getTag($name, $default = 0) {
  323.         if(empty($this -> $name)) {
  324.             return $default ;
  325.         } else {
  326.             return $this -> $name ;
  327.         }
  328.     }        
  329.     
  330.     /**
  331.      * update the id3v1 tags on the file.
  332.      * Note: If/when ID3v2 is implemented this method will probably get another
  333.      *       parameters.
  334.      *     
  335.      * @param boolean $v1   if true update/create an id3v1 tag on the file. (defaults to true)
  336.      * 
  337.      * @access public
  338.      */
  339.     function write($v1 = true) {
  340.     if ($this->debug) print($this->debugbeg . "write()<HR>\n");
  341.     if ($v1) {
  342.         $this->_write_v1();
  343.     }
  344.     if ($this->debug) print($this->debugend);
  345.     } // write()
  346.  
  347.     /**
  348.      * study() - does extra work to get the MPEG frame info.
  349.      * 
  350.      * @access public
  351.      */
  352.     function study() {
  353.     $this->studied = true;
  354.     $this->_readframe();
  355.     } // study()
  356.  
  357.     /**
  358.      * copy($from) - set's the ID3 fields to the same as the fields in $from
  359.      * 
  360.      * @param string    $from   fields to copy
  361.      * @access public
  362.      */
  363.     function copy($from) {
  364.     if ($this->debug) print($this->debugbeg . "copy(\$from)<HR>\n");
  365.     $this->name = $from->name;
  366.     $this->artists  = $from->artists;
  367.     $this->album    = $from->album;
  368.     $this->year = $from->year;
  369.     $this->comment  = $from->comment;
  370.     $this->track    = $from->track;
  371.     $this->genre    = $from->genre;
  372.     $this->genreno  = $from->genreno;
  373.     if ($this->debug) print($this->debugend);
  374.     } // copy($from)
  375.  
  376.     /**
  377.      * remove - removes the id3 tag(s) from a file.
  378.      *
  379.      * @param boolean   $id3v1  true to remove the tag
  380.      * @param boolean   $id3v2  true to remove the tag (Not yet implemented)
  381.      *
  382.      * @access public
  383.      */
  384.     function remove($id3v1 = true, $id3v2 = true) {
  385.     if ($this->debug) print($this->debugbeg . "remove()<HR>\n");
  386.  
  387.     if ($id3v1) {
  388.         $this->_remove_v1();
  389.     }
  390.  
  391.     if ($id3v2) {
  392.         // TODO: write ID3v2 code
  393.     }
  394.  
  395.     if ($this->debug) print($this->debugend);
  396.     } // remove
  397.  
  398.  
  399.     /**
  400.      * read a ID3 v1 or v1.1 tag from a file
  401.      *
  402.      * $file should be the path to the mp3 to look for a tag.
  403.      * When in doubt use the full path.
  404.      *
  405.      * @return mixed    PEAR_Error if fails
  406.      * @access private
  407.      */
  408.     function _read_v1() {
  409.     if ($this->debug) print($this->debugbeg . "_read_v1()<HR>\n");
  410.  
  411.     if (! ($f = @fopen($this->file, 'rb')) ) {
  412.         return PEAR::raiseError( "Unable to open " . $this->file, PEAR_MP3_ID_FNO);
  413.     }
  414.  
  415.     if (fseek($f, -128, SEEK_END) == -1) {
  416.         return PEAR::raiseError( 'Unable to see to end - 128 of ' . $this->file, PEAR_MP3_ID_RE);
  417.     }
  418.  
  419.     $r = fread($f, 128);
  420.     fclose($f);
  421.  
  422.     if ($this->debug) {
  423.         $unp = unpack('H*raw', $r);
  424.         print_r($unp);
  425.     }
  426.  
  427.     $id3tag = $this->_decode_v1($r);
  428.  
  429.     if(!PEAR::isError( $id3tag)) {
  430.         $this->id3v1 = true;
  431.  
  432.         $tmp = explode(Chr(0), $id3tag['NAME']);
  433.         $this->name = $tmp[0];
  434.  
  435.         $tmp = explode(Chr(0), $id3tag['ARTISTS']);
  436.         $this->artists = $tmp[0];
  437.  
  438.         $tmp = explode(Chr(0), $id3tag['ALBUM']);
  439.         $this->album = $tmp[0];
  440.  
  441.         $tmp = explode(Chr(0), $id3tag['YEAR']);
  442.         $this->year = $tmp[0];
  443.  
  444.         $tmp = explode(Chr(0), $id3tag['COMMENT']);
  445.         $this->comment = $tmp[0];
  446.  
  447.         if (isset($id3tag['TRACK'])) {
  448.         $this->id3v11 = true;
  449.         $this->track = $id3tag['TRACK'];
  450.         }
  451.  
  452.         $this->genreno = $id3tag['GENRENO'];
  453.         $this->genre = $id3tag['GENRE'];
  454.     } else {
  455.         return $id3tag ;
  456.         }
  457.  
  458.     if ($this->debug) print($this->debugend);
  459.     } // _read_v1()
  460.  
  461.     /**
  462.      * decodes that ID3v1 or ID3v1.1 tag
  463.      *
  464.      * false will be returned if there was an error decoding the tag
  465.      * else an array will be returned
  466.      *
  467.      * @param   string  $rawtag    tag to decode
  468.      * @return  string  decoded tag
  469.      * @access  private
  470.      */
  471.     function _decode_v1($rawtag) {
  472.     if ($this->debug) print($this->debugbeg . "_decode_v1(\$rawtag)<HR>\n");
  473.  
  474.     if ($rawtag[125] == Chr(0) and $rawtag[126] != Chr(0)) {
  475.         // ID3 v1.1
  476.         $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO';
  477.     } else {
  478.         // ID3 v1
  479.         $format = 'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
  480.     }
  481.  
  482.     $id3tag = unpack($format, $rawtag);
  483.     if ($this->debug) print_r($id3tag);
  484.  
  485.     if ($id3tag['TAG'] == 'TAG') {
  486.         $id3tag['GENRE'] = $this->getgenre($id3tag['GENRENO']);
  487.     } else {
  488.         $id3tag = PEAR::raiseError( 'TAG not found', PEAR_MP3_ID_TNF);
  489.     }
  490.     if ($this->debug) print($this->debugend);
  491.     return $id3tag;
  492.     } // _decode_v1()
  493.  
  494.  
  495.     /**
  496.      * writes a ID3 v1 or v1.1 tag to a file
  497.      *
  498.      * @return mixed    returns PEAR_Error when fails
  499.      * @access private
  500.      */
  501.     function _write_v1() {
  502.     if ($this->debug) print($this->debugbeg . "_write_v1()<HR>\n");
  503.  
  504.     $file = $this->file;
  505.  
  506.     if (! ($f = @fopen($file, 'r+b')) ) {
  507.         return PEAR::raiseError( "Unable to open " . $file, PEAR_MP3_ID_FNO);
  508.     }
  509.  
  510.     if (fseek($f, -128, SEEK_END) == -1) {
  511. //        $this->error = 'Unable to see to end - 128 of ' . $file;
  512.         return PEAR::raiseError( "Unable to see to end - 128 of " . $file, PEAR_MP3_ID_RE);
  513.     }
  514.  
  515.     $this->genreno = $this->getgenreno($this->genre, $this->genreno);
  516.  
  517.     $newtag = $this->_encode_v1();
  518.  
  519.     $r = fread($f, 128);
  520.  
  521.     if ( !PEAR::isError( $this->_decode_v1($r))) {
  522.         if (fseek($f, -128, SEEK_END) == -1) {
  523. //        $this->error = 'Unable to see to end - 128 of ' . $file;
  524.         return PEAR::raiseError( "Unable to see to end - 128 of " . $file, PEAR_MP3_ID_RE);
  525.         }
  526.         fwrite($f, $newtag);
  527.     } else {
  528.         if (fseek($f, 0, SEEK_END) == -1) {
  529. //        $this->error = 'Unable to see to end of ' . $file;
  530.         return PEAR::raiseError( "Unable to see to end of " . $file, PEAR_MP3_ID_RE);
  531.         }
  532.         fwrite($f, $newtag);
  533.     }
  534.     fclose($f);
  535.  
  536.  
  537.     if ($this->debug) print($this->debugend);
  538.     } // _write_v1()
  539.  
  540.     /*
  541.      * encode the ID3 tag
  542.      *
  543.      * the newly built tag will be returned
  544.      *
  545.      * @return string the new tag
  546.      * @access private
  547.      */
  548.     function _encode_v1() {
  549.     if ($this->debug) print($this->debugbeg . "_encode_v1()<HR>\n");
  550.  
  551.     if ($this->track) {
  552.         // ID3 v1.1
  553.         $id3pack = 'a3a30a30a30a4a28x1C1C1';
  554.         $newtag = pack($id3pack,
  555.             'TAG',
  556.             $this->name,
  557.             $this->artists,
  558.             $this->album,
  559.             $this->year,
  560.             $this->comment,
  561.             $this->track,
  562.             $this->genreno
  563.               );
  564.     } else {
  565.         // ID3 v1
  566.         $id3pack = 'a3a30a30a30a4a30C1';
  567.         $newtag = pack($id3pack,
  568.             'TAG',
  569.             $this->name,
  570.             $this->artists,
  571.             $this->album,
  572.             $this->year,
  573.             $this->comment,
  574.             $this->genreno
  575.               );
  576.     }
  577.  
  578.     if ($this->debug) {
  579.         print('id3pack: ' . $id3pack . "\n");
  580.         $unp = unpack('H*new', $newtag);
  581.         print_r($unp);
  582.     }
  583.  
  584.     if ($this->debug) print($this->debugend);
  585.     return $newtag;
  586.     } // _encode_v1()
  587.  
  588.     /**
  589.      * if exists it removes an ID3v1 or v1.1 tag
  590.      *
  591.      * returns true if the tag was removed or none was found
  592.      * else false if there was an error
  593.      * 
  594.      * @return boolean true, if the tag was removed
  595.      * @access private
  596.      */
  597.     function _remove_v1() {
  598.     if ($this->debug) print($this->debugbeg . "_remove_v1()<HR>\n");
  599.  
  600.     $file = $this->file;
  601.  
  602.     if (! ($f = fopen($file, 'r+b')) ) {
  603.         return PEAR::raiseError( "Unable to open " . $file, PEAR_MP3_ID_FNO);
  604.     }
  605.  
  606.     if (fseek($f, -128, SEEK_END) == -1) {
  607.         return PEAR::raiseError( 'Unable to see to end - 128 of ' . $file, PEAR_MP3_ID_RE);
  608.     }
  609.  
  610.     $r = fread($f, 128);
  611.  
  612.     $success = false;
  613.     if ( !PEAR::isError( $this->_decode_v1($r))) {
  614.         $size = filesize($this->file) - 128;
  615.         if ($this->debug) print('size: old: ' . filesize($this->file));
  616.         $success = ftruncate($f, $size);    
  617.         clearstatcache();
  618.         if ($this->debug) print(' new: ' . filesize($this->file));
  619.     }
  620.     fclose($f);
  621.     if ($this->debug) print($this->debugend);
  622.     return $success;
  623.     } // _remove_v1()
  624.  
  625.     /**
  626.     * reads a frame from the file
  627.     *
  628.     * @return mixed PEAR_Error when fails
  629.     * @access private
  630.     */
  631.     function _readframe() {
  632.     if ($this->debug) print($this->debugbeg . "_readframe()<HR>\n");
  633.  
  634.     $file = $this->file;
  635.  
  636.     if (! ($f = fopen($file, 'rb')) ) {
  637.         if ($this->debug) print($this->debugend);
  638.         return PEAR::raiseError( "Unable to open " . $file, PEAR_MP3_ID_FNO) ;
  639.     }
  640.  
  641.     $this->filesize = filesize($file);
  642.  
  643.     do {
  644.         while (fread($f,1) != Chr(255)) { // Find the first frame
  645.         if ($this->debug) echo "Find...\n";
  646.         if (feof($f)) {
  647.             if ($this->debug) print($this->debugend);
  648.             return PEAR::raiseError( "No mpeg frame found", PEAR_MP3_ID_NOMP3) ;
  649.         }
  650.         }
  651.         fseek($f, ftell($f) - 1); // back up one byte
  652.  
  653.         $frameoffset = ftell($f);
  654.  
  655.         $r = fread($f, 4);
  656.         // Binary to Hex to a binary sting. ugly but best I can think of.
  657.         $bits = unpack('H*bits', $r);
  658.         $bits =  base_convert($bits['bits'],16,2);
  659.     } while (!$bits[8] and !$bits[9] and !$bits[10]); // 1st 8 bits true from the while
  660.     if ($this->debug) print('Bits: ' . $bits . "\n");
  661.  
  662.     $this->frameoffset = $frameoffset;
  663.  
  664.     fclose($f);
  665.  
  666.     if ($bits[11] == 0) {
  667.         $this->mpeg_ver = "2.5";
  668.         $bitrates = array(
  669.             '1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
  670.             '2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
  671.             '3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
  672.                  );
  673.     } else if ($bits[12] == 0) {
  674.         $this->mpeg_ver = "2";
  675.         $bitrates = array(
  676.             '1' => array(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0),
  677.             '2' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
  678.             '3' => array(0,  8, 16, 24, 32, 40, 48,  56,  64,  80,  96, 112, 128, 144, 160, 0),
  679.                  );
  680.     } else {
  681.         $this->mpeg_ver = "1";
  682.         $bitrates = array(
  683.             '1' => array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0),
  684.             '2' => array(0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
  685.             '3' => array(0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0),
  686.                  );
  687.     }
  688.     if ($this->debug) print('MPEG' . $this->mpeg_ver . "\n");
  689.  
  690.     $layer = array(
  691.         array(0,3),
  692.         array(2,1),
  693.               );
  694.     $this->layer = $layer[$bits[13]][$bits[14]];
  695.     if ($this->debug) print('layer: ' . $this->layer . "\n");
  696.  
  697.     if ($bits[15] == 0) {
  698.         // It's backwards, if the bit is not set then it is protected.
  699.         if ($this->debug) print("protected (crc)\n");
  700.         $this->crc = true;
  701.     }
  702.  
  703.     $bitrate = 0;
  704.     if ($bits[16] == 1) $bitrate += 8;
  705.     if ($bits[17] == 1) $bitrate += 4;
  706.     if ($bits[18] == 1) $bitrate += 2;
  707.     if ($bits[19] == 1) $bitrate += 1;
  708.     $this->bitrate = $bitrates[$this->layer][$bitrate];
  709.  
  710.     $frequency = array(
  711.         '1' => array(
  712.             '0' => array(44100, 48000),
  713.             '1' => array(32000, 0),
  714.                 ),
  715.         '2' => array(
  716.             '0' => array(22050, 24000),
  717.             '1' => array(16000, 0),
  718.                 ),
  719.         '2.5' => array(
  720.             '0' => array(11025, 12000),
  721.             '1' => array(8000, 0),
  722.                   ),
  723.           );
  724.     $this->frequency = $frequency[$this->mpeg_ver][$bits[20]][$bits[21]];
  725.  
  726.     $this->padding = $bits[22];
  727.     $this->private = $bits[23];
  728.  
  729.     $mode = array(
  730.         array('Stereo', 'Joint Stereo'),
  731.         array('Dual Channel', 'Mono'),
  732.              );
  733.     $this->mode = $mode[$bits[24]][$bits[25]];
  734.  
  735.     // XXX: I dunno what the mode extension is for bits 26,27
  736.  
  737.     $this->copyright = $bits[28];
  738.     $this->original = $bits[29];
  739.  
  740.     $emphasis = array(
  741.         array('none', '50/15ms'),
  742.         array('', 'CCITT j.17'),
  743.              );
  744.     $this->emphasis = $emphasis[$bits[30]][$bits[31]];
  745.  
  746.     if ($this->bitrate == 0) {
  747.         $s = -1;
  748.     } else {
  749.         $s = ((8*filesize($this->file))/1000) / $this->bitrate;        
  750.     }
  751.     $this->length = sprintf('%02d:%02d',floor($s/60),floor($s-(floor($s/60)*60)));
  752.     $this->lengths = (int)$s;
  753.  
  754.     if ($this->debug) print($this->debugend);
  755.     } // _readframe()
  756.  
  757.     /**
  758.      * getGenre - return the name of a genre number
  759.      *
  760.      * if no genre number is specified the genre number from
  761.      * $this->genreno will be used.
  762.      *
  763.      * the genre is returned or false if an error or not found
  764.      * no error message is ever returned
  765.      *
  766.      * @param   integer $genreno Number of the genre
  767.      * @return  mixed   false, if no genre found, else string
  768.      *
  769.      * @access public     
  770.      */
  771.     function getGenre($genreno) {
  772.     if ($this->debug) print($this->debugbeg . "getgenre($genreno)<HR>\n");
  773.  
  774.     $genres = $this->genres();
  775.     if (isset($genres[$genreno])) {
  776.         $genre = $genres[$genreno];
  777.         if ($this->debug) print($genre . "\n");
  778.     } else {
  779.         $genre = '';
  780.     }
  781.  
  782.     if ($this->debug) print($this->debugend);
  783.     return $genre;
  784.     } // getGenre($genreno)
  785.  
  786.     /*
  787.      * getGenreNo - return the number of the genre name
  788.      *
  789.      * the genre number is returned or 0xff (255) if a match is not found
  790.      * you can specify the default genreno to use if one is not found
  791.      * no error message is ever returned
  792.      *
  793.      * @param   string  $genre      Name of the genre
  794.      * @param   integer $default    Genre number in case of genre not found
  795.      *
  796.      * @access public
  797.      */
  798.     function getGenreNo($genre, $default = 0xff) {
  799.     if ($this->debug) print($this->debugbeg . "getgenreno('$genre',$default)<HR>\n");
  800.  
  801.     $genres = $this->genres();
  802.     $genreno = false;
  803.     if ($genre) {
  804.         foreach ($genres as $no => $name) {
  805.         if (strtolower($genre) == strtolower($name)) {
  806.             if ($this->debug) print("$no:'$name' == '$genre'");
  807.             $genreno = $no;
  808.         }
  809.         }
  810.     }
  811.     if ($genreno === false) $genreno = $default;
  812.     if ($this->debug) print($this->debugend);
  813.     return $genreno;
  814.     } // getGenreNo($genre, $default = 0xff)
  815.  
  816.     /*
  817.      * genres - returns an array of the ID3v1 genres
  818.      *
  819.      * @return array
  820.      *
  821.      * @access public
  822.      */
  823.     function genres() {
  824.     return array(
  825.         0   => 'Blues',
  826.         1   => 'Classic Rock',
  827.         2   => 'Country',
  828.         3   => 'Dance',
  829.         4   => 'Disco',
  830.         5   => 'Funk',
  831.         6   => 'Grunge',
  832.         7   => 'Hip-Hop',
  833.         8   => 'Jazz',
  834.         9   => 'Metal',
  835.         10  => 'New Age',
  836.         11  => 'Oldies',
  837.         12  => 'Other',
  838.         13  => 'Pop',
  839.         14  => 'R&B',
  840.         15  => 'Rap',
  841.         16  => 'Reggae',
  842.         17  => 'Rock',
  843.         18  => 'Techno',
  844.         19  => 'Industrial',
  845.         20  => 'Alternative',
  846.         21  => 'Ska',
  847.         22  => 'Death Metal',
  848.         23  => 'Pranks',
  849.         24  => 'Soundtrack',
  850.         25  => 'Euro-Techno',
  851.         26  => 'Ambient',
  852.         27  => 'Trip-Hop',
  853.         28  => 'Vocal',
  854.         29  => 'Jazz+Funk',
  855.         30  => 'Fusion',
  856.         31  => 'Trance',
  857.         32  => 'Classical',
  858.         33  => 'Instrumental',
  859.         34  => 'Acid',
  860.         35  => 'House',
  861.         36  => 'Game',
  862.         37  => 'Sound Clip',
  863.         38  => 'Gospel',
  864.         39  => 'Noise',
  865.         40  => 'Alternative Rock',
  866.         41  => 'Bass',
  867.         42  => 'Soul',
  868.         43  => 'Punk',
  869.         44  => 'Space',
  870.         45  => 'Meditative',
  871.         46  => 'Instrumental Pop',
  872.         47  => 'Instrumental Rock',
  873.         48  => 'Ethnic',
  874.         49  => 'Gothic',
  875.         50  => 'Darkwave',
  876.         51  => 'Techno-Industrial',
  877.         52  => 'Electronic',
  878.         53  => 'Pop-Folk',
  879.         54  => 'Eurodance',
  880.         55  => 'Dream',
  881.         56  => 'Southern Rock',
  882.         57  => 'Comedy',
  883.         58  => 'Cult',
  884.         59  => 'Gangsta',
  885.         60  => 'Top 40',
  886.         61  => 'Christian Rap',
  887.         62  => 'Pop/Funk',
  888.         63  => 'Jungle',
  889.         64  => 'Native US',
  890.         65  => 'Cabaret',
  891.         66  => 'New Wave',
  892.         67  => 'Psychadelic',
  893.         68  => 'Rave',
  894.         69  => 'Showtunes',
  895.         70  => 'Trailer',
  896.         71  => 'Lo-Fi',
  897.         72  => 'Tribal',
  898.         73  => 'Acid Punk',
  899.         74  => 'Acid Jazz',
  900.         75  => 'Polka',
  901.         76  => 'Retro',
  902.         77  => 'Musical',
  903.         78  => 'Rock & Roll',
  904.         79  => 'Hard Rock',
  905.         80  => 'Folk',
  906.         81  => 'Folk-Rock',
  907.         82  => 'National Folk',
  908.         83  => 'Swing',
  909.         84  => 'Fast Fusion',
  910.         85  => 'Bebob',
  911.         86  => 'Latin',
  912.         87  => 'Revival',
  913.         88  => 'Celtic',
  914.         89  => 'Bluegrass',
  915.         90  => 'Avantgarde',
  916.         91  => 'Gothic Rock',
  917.         92  => 'Progressive Rock',
  918.         93  => 'Psychedelic Rock',
  919.         94  => 'Symphonic Rock',
  920.         95  => 'Slow Rock',
  921.         96  => 'Big Band',
  922.         97  => 'Chorus',
  923.         98  => 'Easy Listening',
  924.         99  => 'Acoustic',
  925.         100 => 'Humour',
  926.         101 => 'Speech',
  927.         102 => 'Chanson',
  928.         103 => 'Opera',
  929.         104 => 'Chamber Music',
  930.         105 => 'Sonata',
  931.         106 => 'Symphony',
  932.         107 => 'Booty Bass',
  933.         108 => 'Primus',
  934.         109 => 'Porn Groove',
  935.         110 => 'Satire',
  936.         111 => 'Slow Jam',
  937.         112 => 'Club',
  938.         113 => 'Tango',
  939.         114 => 'Samba',
  940.         115 => 'Folklore',
  941.         116 => 'Ballad',
  942.         117 => 'Power Ballad',
  943.         118 => 'Rhytmic Soul',
  944.         119 => 'Freestyle',
  945.         120 => 'Duet',
  946.         121 => 'Punk Rock',
  947.         122 => 'Drum Solo',
  948.         123 => 'Acapella',
  949.         124 => 'Euro-House',
  950.         125 => 'Dance Hall',
  951.         126 => 'Goa',
  952.         127 => 'Drum & Bass',
  953.         128 => 'Club-House',
  954.         129 => 'Hardcore',
  955.         130 => 'Terror',
  956.         131 => 'Indie',
  957.         132 => 'BritPop',
  958.         133 => 'Negerpunk',
  959.         134 => 'Polsk Punk',
  960.         135 => 'Beat',
  961.         136 => 'Christian Gangsta Rap',
  962.         137 => 'Heavy Metal',
  963.         138 => 'Black Metal',
  964.         139 => 'Crossover',
  965.         140 => 'Contemporary Christian',
  966.         141 => 'Christian Rock',
  967.         142 => 'Merengue',
  968.         143 => 'Salsa',
  969.         144 => 'Trash Metal',
  970.         145 => 'Anime',
  971.         146 => 'Jpop',
  972.         147 => 'Synthpop'
  973.             );
  974.     } // genres
  975. } // end of id3
  976.  
  977. ?>